package edu.ubb.remeye.client.communication;

import java.net.InetSocketAddress;

import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.service.IoConnector;
import org.apache.mina.core.service.IoHandler;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.statemachine.StateMachine;
import org.apache.mina.statemachine.StateMachineFactory;
import org.apache.mina.statemachine.StateMachineProxyBuilder;
import org.apache.mina.statemachine.annotation.IoHandlerTransition;
import org.apache.mina.statemachine.context.IoSessionStateContextLookup;
import org.apache.mina.statemachine.context.StateContext;
import org.apache.mina.statemachine.context.StateContextFactory;
import org.apache.mina.transport.socket.nio.NioSocketConnector;

import edu.ubb.remeye.client.Client;
import edu.ubb.remeye.client.communication.handler.ClientStateHandler;
import edu.ubb.remeye.client.settings.ClientSetter;
import edu.ubb.remeye.communication.message.codec.MessageCodecFactory;

/**
 * Client class for communication.
 * 
 * @author Andrew Ianicicau
 *
 */
public class RemEyeClient implements Client {

	///////////////////////////////////////////////////////////////////////////////////
	//// ATTRIBUTES - RemEyeClient
	///////////////////////////////////////////////////////////////////////////////////

	/**
	 * Client attribute for singleton design pattern.
	 */
	private static RemEyeClient client;
	
	/**
	 * The server's IP address. Default IP address is localhost.
	 */
	private String serverIP;

	/**
	 * The port on which the server accepts connections. Default port is 5900.
	 */
	private int serverPort;

	/**
	 * The client socket for connecting to server using Apache MINA IoConnector.
	 */
	private IoConnector clientConnector;
	
	/**
	 * The status of client.
	 */
	private boolean clientRunning;
	
	
	
	///////////////////////////////////////////////////////////////////////////////////
	//// CONSTRUCTORS - RemEyeClient
	///////////////////////////////////////////////////////////////////////////////////

	/**
	 * Default constructor.
	 */
	private RemEyeClient() {
		clientRunning = false;
	}

	
	
	///////////////////////////////////////////////////////////////////////////////////
	//// METHODS - RemEyeClient
	///////////////////////////////////////////////////////////////////////////////////

	/**
	 * Getting server instance.
	 */
	public static RemEyeClient getClient() {
		if(client == null)
			client = new RemEyeClient();
		return client;
	}
	
	/**
	 * Start client.
	 */
	@Override
	public void connectClient() {
		if(!client.clientRunning) {
			// saving client settings
			ClientSetter.getInstance().saveSettings();
			
			// getting settings for socket initialization
			client.clientConnector = new NioSocketConnector();
			client.clientConnector.setConnectTimeoutMillis(10000);
			client.serverIP = ClientSetter.getInstance().getIp();
			client.serverPort = ClientSetter.getInstance().getPort();
			client.clientConnector.setHandler(createClientIoHandler());
			
			// adding message codec factory
			client.clientConnector.getFilterChain().addLast("message", new ProtocolCodecFilter(new MessageCodecFactory()));
			
			// setting connection address
			ConnectFuture future = client.clientConnector.connect(new InetSocketAddress(client.serverIP, client.serverPort));
			future.awaitUninterruptibly();
			
			client.clientRunning = true;
		}
	}

	/**
	 * Stop client.
	 */
	@Override
	public void disconnectClient() {
		if(client.clientRunning) {
			client.clientConnector.dispose();
			client.clientRunning = false;
		}
	}

	/**
	 * Creating Apache MINA IoHandler with Apache MINA State Machine for client.
	 * @return IoHandler with state machine.
	 */
	private IoHandler createClientIoHandler() {
		// client state machine
		StateMachine sm = StateMachineFactory.getInstance(IoHandlerTransition.class).create(ClientStateHandler.BASE, new ClientStateHandler(this));

		return new StateMachineProxyBuilder().setStateContextLookup(
				new IoSessionStateContextLookup(new StateContextFactory() {

					@Override
					public StateContext create() {
						return new ClientStateHandler.ClientContext();
					}
					
				})).create(IoHandler.class, sm);
	}

}
